package ru.amse.baltijsky.javascheme.tree;

import java.util.*;

/**
 * Represents a branching (conditional) node of the internal schema tree.
 * Branches are also treated as nested constructions to simplify the generation of the tree.
 * Additonally stores conditions for all branches.
 */
public abstract class BranchNode extends SchemaNode {
    /**
     * List of conditions (taken verbatim from the client source code)
     * that must be fulfilled for the program flow to proceed to the corresponding branch.
     * A condition is itself a list.
     */
    private final List<List<String>> conditions = new LinkedList<List<String>>();
    /**
     * Label attached to the node as the statement enclosing other statements.
     */
    private String label;

    /**
     * Default constructor. Creates a node without previous or parent (nesting) nodes and associated code.
     */
    public BranchNode() {
        super();
    }

    /**
     * Constructs a branching node with the specified parent and previous nodes with some basic setup.
     *
     * @param parentNode node that nests our node
     * @param prevNode   previously constructed node (the current node will be reachable from it via "next" link)
     */
    public BranchNode(SchemaNode parentNode, SchemaNode prevNode) {
        super(parentNode, prevNode);
    }

    /**
     * Constructs a new node without previous or nesting node and with information about the corresponding code.
     *
     * @param s code associated with the node
     */
    public BranchNode(String[] s) {
        super(s);
    }

    /**
     * Constructs a new node with full information about the nesting and previous nodes and the corresponding code.
     *
     * @param parentNode link to the parent (nesting) node
     * @param prevNode   link to the node that is previous in the sense of program flow
     * @param s          code associated with the node
     */
    public BranchNode(SchemaNode parentNode, SchemaNode prevNode, String[] s) {
        super(parentNode, prevNode, s);
    }

    /**
     * Determines whether the node has the conditions list.
     *
     * @return true iff the node has at least one condition
     */
    public boolean hasConditions() {
        return !conditions.isEmpty();
    }

    public Iterator<List<String>> getConditions() {
        return Collections.unmodifiableCollection(conditions).iterator();
    }

    /**
     * Adds a new condition for a new branch.
     *
     * @param condition the new condition
     */
    public void addConditionToCurrentSet(String condition) {
        conditions.get(conditions.size() - 1).add(condition);
    }

    public void addNewConditionSet() {
        conditions.add(new ArrayList<String>());
    }

    public void addNewConditionSet(String[] conditions) {
        this.conditions.add(Arrays.asList(conditions));
    }

    public void addConditionToNewSet(String condition) {
        addNewConditionSet();
        addConditionToCurrentSet(condition);
    }

    public String getLabel() {
        return label;
    }

    public void setLabel(String label) {
        this.label = label;
    }

    @Override
    public <T, C, E extends Throwable> T accept(TreeVisitor<T, C, E> visitor, C context) throws E {
        return visitor.visit(this, context);
    }
}
